package com.atguigu.tingshu.album.service.impl;

import com.atguigu.tingshu.album.mapper.AlbumAttributeValueMapper;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.common.caChe.TsCache;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumAttributeValueVo;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.Nullable;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {

	@Autowired
	private AlbumInfoMapper albumInfoMapper;

	@Autowired
	private AlbumAttributeValueMapper albumAttributeValueMapper;

	@Autowired
	private AlbumStatMapper albumStatMapper;

	@Autowired
	private RabbitService rabbitService;

	@Autowired
	private RedisTemplate redisTemplate;

	@Autowired
	private RedissonClient redissonClient;
	/**
	 * 保存专辑
	 * @param albumInfoVo
	 * @return
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public Boolean saveAlbumInfo(AlbumInfoVo albumInfoVo,Long userId) {
		/*
			album_attribute_value
			album_info
			album_stat
		 */

		try {
			// 1.先保存专辑信息表（album_info）
			AlbumInfo albumInfo = new AlbumInfo();
			// 属性赋值（复制）：前提条件是属性名必须一致！也可以用传统的set方法
			BeanUtils.copyProperties(albumInfoVo,albumInfo);
			// 前端传递的数据是否都要保存到数据表或保存数据表中的数据，前端都传递了么？校验！
			// userId（需要知道当前专辑是谁创建的），tracks_for_free（付费专辑的时候，需要先让用户进行试听！），status（审核状态）
			// 这三个值前端没传就需要自己单独进行赋值
			albumInfo.setUserId(userId);
			// 判断专辑是否免费
			if(!SystemConstant.ALBUM_PAY_TYPE_FREE.equals(albumInfo.getPayType())){
				// 需要付费（设置试听集数）
				albumInfo.setTracksForFree(5);
			}
			// 设置审核状态（这里自己给自己审核通过。实际中应该发送一个消息，给管理平台，站内信（提示信息）审核通过：监听消息完成审核状态赋值！）
			albumInfo.setStatus(SystemConstant.ALBUM_STATUS_NO_PASS);
			// 表示执行insert 方法：
			albumInfoMapper.insert(albumInfo);

			// 2.保存标签（album_attribute_value 专辑对应的属性值）
			List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
			// 判断前端传来的专辑属性值集合是否为空
			if(!CollectionUtils.isEmpty(albumAttributeValueVoList)){
				// 不为空，循环遍历，进行赋值
				for (AlbumAttributeValueVo albumAttributeValueVo : albumAttributeValueVoList) {
					// 创建对象
					AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
					// 进行赋值
					albumAttributeValue.setAlbumId(albumInfo.getId());
					albumAttributeValue.setAttributeId(albumAttributeValueVo.getAttributeId());
					albumAttributeValue.setValueId(albumAttributeValueVo.getValueId());
					albumAttributeValueMapper.insert(albumAttributeValue);
				}
				// 上面赋值操作可以进行优化批量保存！（saveBath）
			}

			// 3.保存专辑状态（album_stat）
			saveAlbumStat(albumInfo.getId(),SystemConstant.ALBUM_STAT_PLAY); // 0401 播放量
			saveAlbumStat(albumInfo.getId(),SystemConstant.ALBUM_STAT_SUBSCRIBE); // 0402 订阅量
			saveAlbumStat(albumInfo.getId(),SystemConstant.ALBUM_STAT_BROWSE); // 0403 购买量
			saveAlbumStat(albumInfo.getId(),SystemConstant.ALBUM_STAT_COMMENT); // 0404 评论数

			//  判断当前专辑是公开; is_open=1;
			if ("1".equals(albumInfo.getIsOpen())){
				//  调用上架方法;
				rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM,MqConst.ROUTING_ALBUM_UPPER, albumInfo.getId());
				// 将专辑id添加到布隆过滤器；
				RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
				// 为什么要add添加id？是因为前端用户是通过id来查的
				bloomFilter.add(albumInfo.getId());
			}

		} catch (BeansException e) {
			// 开发中都需要使用这种方式进行日志输出，不能直接使用e.printStackTrance(); 会占用资源！
			log.error(e.getMessage(),e);
			throw new GuiguException(ResultCodeEnum.SERVICE_ERROR);
			// 不可达代码
//			return false;
		}
		return true;
	}


	public void saveAlbumStat(Long albumId, String statPlay) {
		// 创建对象
		AlbumStat albumStat = new AlbumStat();
		albumStat.setAlbumId(albumId);
		albumStat.setStatType(statPlay);
		albumStat.setStatNum(new Random().nextInt(100000));
		albumStatMapper.insert(albumStat);
	}

	/**
	 * 分页查询专辑列表
	 * @param page
	 * @param albumInfoQuery
	 * @return
	 */
	@Override
	public IPage<AlbumListVo> findUserAlbumPage(Page<AlbumListVo> page, AlbumInfoQuery albumInfoQuery) {
		//需要通过两张表进行查询; （album_info 和 album_stat）
		return albumInfoMapper.selectUserAlbumPage(page,albumInfoQuery);
	}

	/**
	 * 根据专辑id查看专辑信息
	 * @param albumId
	 * @return
	 */
	@TsCache(keyPrefix = "album:",keySuffix = ":info")
	@Override
	public AlbumInfo getAlbumInfo(Long albumId) {
		// 数据库兜底操作
		return getAlbumInfoDB(albumId);
		/*// 定义String 数据类型
		String dataKey = RedisConstant.ALBUM_INFO_PREFIX + albumId;
		try {//这个try-catch是防止Redis爆炸，redisTemplate报错，导致if和else谁也不走
			// 根据缓存的key获取数据
			AlbumInfo albumInfo = (AlbumInfo) this.redisTemplate.opsForValue().get(dataKey);
			// 判断缓存中是否存在数据
			if(null == albumId){
				// 不存在；查询数据库
				// 加锁
				String lockKey = RedisConstant.ALBUM_LOCK_SUFFIX + albumId;
				RLock lock = redissonClient.getLock(lockKey);
				lock.lock();
				try {
					// 查询数据库之前，再判断一次缓存（防止每个人都去数据库中进行查询操作）
					albumInfo = (AlbumInfo) this.redisTemplate.opsForValue().get(lockKey);
					if(null != albumInfo){
						return albumInfo;
					}
					// 业务逻辑；
					albumInfo = getAlbumInfoDB(albumId);
					// 判断数据是否为空
					if(null == albumInfo){
						// 防止穿透（缓存和数据库都不存在），设置一个空值
						this.redisTemplate.opsForValue().set(dataKey,new AlbumInfo(),RedisConstant.ALBUM_TEMPORARY_TIMEOUT, TimeUnit.SECONDS);
						return new AlbumInfo();
					}
					// 数据库中有数据
					this.redisTemplate.opsForValue().set(dataKey,albumInfo,RedisConstant.ALBUM_TIMEOUT,TimeUnit.SECONDS);
					// 返回数据
					return albumInfo;
				} catch (Exception e) {
					// 异常信息
					log.error(e.getMessage(),e);
				} finally {
					// 解锁
					lock.unlock();
				}
			}else {
				// 缓存有数据
				System.out.println("缓存有数据");
				return albumInfo;
			}
		} catch (Exception e) {
			log.error(e.getMessage(),e);
		}*/

	}

	private @Nullable AlbumInfo getAlbumInfoDB(Long albumId) {
		//因为需要给专辑标签赋值：albumAttributeValueVoList
		//查询专辑信息表；
		AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
		//为了防止空指针，null写前面
		if(null != albumInfo){
			// 根据专辑Id获取到专辑对应的属性值Id与属性Id的集合数据
			// select * from album_attribute_value where album_id = 1615
			// Warpper构建查询条件
			LambdaQueryWrapper<AlbumAttributeValue> lambdaQueryWrapper = new LambdaQueryWrapper<>();
			lambdaQueryWrapper.eq(AlbumAttributeValue::getAlbumId, albumId);
			List<AlbumAttributeValue> albumAttributeValueList = albumAttributeValueMapper.selectList(lambdaQueryWrapper);
			albumInfo.setAlbumAttributeValueVoList(albumAttributeValueList);
		}
		// 返回数据
		return albumInfo;
	}

	/**
	 * 根据专辑Id更新专辑信息
	 * @param albumId
	 * @param AlbumInfoVo
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void updateAlbumInfo(Long albumId, AlbumInfoVo albumInfoVo) {
		// update album_info set title = ? where id = ?
		AlbumInfo albumInfo1 = new AlbumInfo();
		// 属性拷贝
		BeanUtils.copyProperties(albumInfoVo,albumInfo1);
		albumInfo1.setId(albumId);
		albumInfoMapper.updateById(albumInfo1);
		// album_attribute_value 由于修改值的时候未触发控制器，所以不能实时记录修改的信息。因此要先删除数据，再新增数据！
		// 删除原有标签数据：delete from album_attribute_value where album_id = ?; 物理删除语句但是最后是逻辑删除（delete->update）; 因为实体类中有一个isDelete字段 @TableLogic 逻辑删除！
		albumAttributeValueMapper.delete(new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId,albumId));
		// 获取客户端传过来的albumAttributeValueVoList列表
		List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
		if(!CollectionUtils.isEmpty(albumAttributeValueVoList)){
			// 循环遍历修改数据
			for (AlbumAttributeValueVo albumAttributeValueVo : albumAttributeValueVoList) {
				// 创建修改的实体对象
				AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
				// 属性拷贝
				BeanUtils.copyProperties(albumAttributeValueVo,albumAttributeValue);
				albumAttributeValue.setAlbumId(albumId);
				// 批量插入（新增）
				albumAttributeValueMapper.insert(albumAttributeValue);
			}
		}
		//  判断当前专辑是公开; is_open=1;
		if ("1".equals(albumInfo1.getIsOpen())){
			//  调用上架方法;
			rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM,MqConst.ROUTING_ALBUM_UPPER, albumInfo1.getId());
			// 将专辑id添加到布隆过滤器；
			RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
			// 为什么要add添加id？是因为前端是通过id来查的（过滤的核心是看用户通过根据哪个条件来定的）
			bloomFilter.add(albumInfo1.getId());
		} else {
			//  调用下架方法;
			rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM,MqConst.ROUTING_ALBUM_LOWER, albumInfo1.getId());
		}
	}

	/**
	 * 删除专辑
	 * @param albumId
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void removeAlbumInfo(Long albumId) {
		// 删除album_info
		albumInfoMapper.deleteById(albumId);

		// 删除album_attribute_value
		albumAttributeValueMapper.delete(new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId,albumId));

		//删除album_stat
		albumStatMapper.delete(new LambdaQueryWrapper<AlbumStat>().eq(AlbumStat::getAlbumId,albumId));
	}

	/**
	 * 获取当前用户所有专辑列表
	 * @return
	 */
	@Override
	public List<AlbumInfo> findUserAllAlbumList(Long userId) {
		// 构建查询条件
		LambdaQueryWrapper<AlbumInfo> albumInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
		// select * from album_info where user_id = 1 order by id desc limit 100;
		albumInfoLambdaQueryWrapper.eq(AlbumInfo::getUserId, userId);
		albumInfoLambdaQueryWrapper.orderByDesc(AlbumInfo::getId);
		Page<AlbumInfo> albumInfoPage = new Page<>(1, 100);
//		List<AlbumInfo> albumInfoList = albumInfoMapper.selectList(albumInfoLambdaQueryWrapper);
		Page<AlbumInfo> infoPage = albumInfoMapper.selectPage(albumInfoPage, albumInfoLambdaQueryWrapper);
		// 返回专辑列表（getRecords() 方法用于获取这个分页对象中实际的记录列表（即当前页的数据））
		return infoPage.getRecords();
	}


	/**
	 * 根据专辑id获取专辑统计信息
	 * @param albumId
	 * @return
	 */
	@Override
	@TsCache(keyPrefix = "statVo:")
	public AlbumStatVo getAlbumStat(Long albumId) {
		// 调用mapper层方法
		return albumInfoMapper.getAlbumStat(albumId);
	}
}
